﻿using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace Sfd.Services.Synchronizers
{
    public class AzureBlobSynchronizer
    {
        public const string LastModifiedUtcMetadataKey = "LastModifiedUtcTicks";

        private readonly string _targetContainer;
        private readonly CloudBlobClient _blobStorage;


        public AzureBlobSynchronizer(string targetContainer, CloudStorageAccount storageAccount)
        {
            _blobStorage = storageAccount.CreateCloudBlobClient();
            _targetContainer = targetContainer;

            CreateContainer();
        }

        public CloudBlobContainer Container
        {
            get { return _blobStorage.GetContainerReference(_targetContainer); }
        }

        private void CreateContainer()
        {
            //
            // Azure Blob Storage requires some delay between deleting and recreating containers.  This loop 
            // compensates for that delay if the first attempt to creat the container fails then wait 30 
            // seconds and try again.  While the service actually returns error 409 with the correct description
            // of the failure the storage client interprets that as the container existing and the
            // call to CreateContainer returns false.  For now just work around this by attempting to create
            // a test blob in the container bofore proceeding.
            //
            for (int i = 1; i <= 10; i++)
            {
                CloudBlobContainer blobContainer = Container;
                blobContainer.CreateIfNotExist();

                var stream = new MemoryStream();
                try
                {
                    blobContainer.GetBlobReference("__testblob").UploadFromStream(stream);
                    blobContainer.GetBlobReference("__testblob").Delete();

                    var permissions = blobContainer.GetPermissions();
                    permissions.PublicAccess = BlobContainerPublicAccessType.Container;
                    blobContainer.SetPermissions(permissions);

                    break;
                }
                catch (StorageClientException e)
                {
                    Console.WriteLine(e.Message);
                    Console.WriteLine("Failed creating container, retrying in 30 seconds...");

                    // Wait 10 seconds and retry
                    System.Threading.Thread.Sleep(10000);
                    if (i == 10)
                    {
                        // Only retry 10 times
                        throw;
                    }
                }
            }
        }

        public Dictionary<string, SyncItem> GetSyncItems()
        {
            var items = new Dictionary<string, SyncItem>();

            // Include all items in the listing including those with path like ('/') file names
            var opts = new BlobRequestOptions {UseFlatBlobListing = true};

            foreach (var o in Container.ListBlobs(opts))
            {
                var syncItem = CreateSyncItem(o.Uri.ToString());
                if (syncItem != null)
                    items.Add(syncItem.Key, syncItem);
            }

            return items;
        }

        public Dictionary<string, SyncItem> GetSyncItems(IEnumerable<FileSyncItem> files)
        {
            var items = new Dictionary<string, SyncItem>();

            foreach (var fileSyncItem in files)
            {
                var syncItem = CreateSyncItem(string.Concat(Container.Uri.ToString(), "/", fileSyncItem.Key));
                if (syncItem != null)
                    items.Add(syncItem.Key, syncItem);
            }
            return items;
        }

        public static bool Exists(CloudBlob blob)
        {
            try
            {
                blob.FetchAttributes();
                return true;
            }
            catch (StorageClientException e)
            {
                if (e.ErrorCode == StorageErrorCode.ResourceNotFound)
                {
                    return false;
                }

                throw;
            }
        }

        public AzureBlobSyncItem CreateSyncItem(string url)
        {
            var blob = Container.GetBlobReference(url);
            if (!Exists(blob))
                return null;

            long lastModificationUtcTicks;
            // si on a stockée la date de dernière modification dans les datas, on la récupère
            if (!long.TryParse(blob.Attributes.Metadata[LastModifiedUtcMetadataKey], out lastModificationUtcTicks))
                lastModificationUtcTicks = blob.Properties.LastModifiedUtc.Ticks;

            return new AzureBlobSyncItem
                       {
                           Key =
                               blob.Uri.ToString().Substring(
                                   blob.Container.Uri.ToString().Length + 1).ToLower(),
                           Blob = blob,
                           LastModificationDateTicks = lastModificationUtcTicks,
                           Length = blob.Properties.Length
                       };
        }
    }
}
